#include <phostpdk.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>

#define MAX_CONFIG_LINE 80  /* Random stupid number */

static char *sProgName;         /**< Name of the program, for help screen.  */
static char sReadonly = 0;      /**< Nonzero for "dry-run" (read-only). */
static char sVerbose = 0;       /**< Nonzero to be more verbose. */

RaceType_Def tantOwner;
Uns16 turnsOwned; /* Nobody sane would want a game to extend past 65535 turns. */
Uns16 tantPlanet;

struct {
    Uns16 turnsOwned;
    int minDistance;
    Boolean allies;
} config;
    
/**************************************************************************************************************/
/** Read configuration data from file. */
void
ReadTantalusConfig(void)
{
    FILE *F;

    config.allies = True;
    config.minDistance = 0;
    config.turnsOwned = 20;

    F = OpenInputFile("tantalus.cfg", GAME_OR_ROOT_DIR | TEXT_MODE | NO_MISSING_ERROR);

    if (F != NULL) {
        Info("Reading settings from config file.");
        while (!feof(F)) {
            int i;
            char buf[MAX_CONFIG_LINE];

            fgets(buf, sizeof(buf), F);

            if (sscanf(buf, "Allies=%d", &i)) {
                config.allies = (i != 0);
            } else if (sscanf(buf,"MinDistance=%d", &i)) {
                if (i <= 65000)
                    config.minDistance = i;
                else
                    ErrorExit("MinDistance must be less than 65000.");
            } else if (sscanf(buf,"TurnsOwned=%d", &i)) {
                if (i <= 65000)
                    config.turnsOwned = i;
                else
                    ErrorExit("TurnsOwned must be less than 65000.");
            }
        }
        fclose(F);
    } else {
        Info("Couldn't open tantalus.cfg file for reading.");
        Info("Using defaults.");
    }

    if (sVerbose) {
        Info("Settings:");
        Info("  Allies=%d", config.allies);
        Info("  MinDistance=%d", config.minDistance);
        Info("  TurnsOwned=%d", config.turnsOwned);
    }
}    

/**************************************************************************************************************/
/** Process Tantalus planet ownership. */
void
ProcessTantalus(void)
{
    Boolean allied = True;
    Uns16 newOwner;
    newOwner = PlanetOwner(tantPlanet+1);

    if (newOwner == NoOwner) {
        turnsOwned = 0;
        tantOwner = newOwner;
        return;
    }

    turnsOwned++;
    if (tantOwner == newOwner) {
        return;
    }

    if (!PlayersAreAllies(tantOwner, newOwner))
       allied = False;
    if (!PlayerAllowsAlly(tantOwner, newOwner, ALLY_PLANETS))
       allied = False;
    if (!PlayerAllowsAlly(tantOwner, newOwner, ALLY_COMBAT))
       allied = False;

    if (!allied)
        turnsOwned = 1;

    tantOwner = newOwner;
}
    
/**************************************************************************************************************/
/** Dump owner info */
void
InfoTantalus(void)
{
    const char *name = RaceShortName(tantOwner, NULL);
    Info("Owner: %s", name?name:"Nobody");
    Info("Turns Owned: %d", turnsOwned);
}

/**************************************************************************************************************/
/** Initialize Tantalus planet and ownership.  Read from data file.  If file doesn't exist
    pick a planet at random, and set ownership to 'nobody'. */
void
InitializeTantalus(void)
{
    FILE *F;

    Info("Initializing Tantalus Device.");

    /** Initialize to zero before begining. */
    tantOwner = NoOwner;
    turnsOwned = 0;

    ReadTantalusConfig();
    /** Read tantalus data in from data file. */
    F = OpenInputFile("tantalus.hst", GAME_DIR_ONLY | NO_MISSING_ERROR);
    if (F != NULL) {
        int temp;
        Boolean error;
        
        Info("Reading tantalus data from file.");
        
        error = False;
        if (!DOSRead16(&tantPlanet, 1, F))
            error = True;
        temp = fgetc(F);
        if (temp != EOF)
            tantOwner = temp;
        else
            error = True;
        if (!DOSRead16(&turnsOwned, 1, F))
            error = True;

        fclose(F);
        
        if (error)        
            ErrorExit("Unable to read tantalus data.");
            
    } else {
        Uns16 planets[PLANET_NR];
        RandType_Def candidates = 0; /* RandType_Def must be larger than 9 bits (depending on PLANET_NR) */
        Uns16 i;
        Int32 distSq = config.minDistance * config.minDistance;
        
        Info("Couldn't open tantalus.hst file for reading.");
        Info("Choosing random planet...");
        
        /* O(n^2), but only done once a game. */
        
        for (i=0; i<PLANET_NR; i++) {
            if (PlanetOwner(i+1) == NoOwner) {
                Boolean valid = True;
                if (config.minDistance > 0) {
                    int j;
                    int x = PlanetLocationX(i+1);
                    int y = PlanetLocationY(i+1);
                    
                    for (j=0; j<PLANET_NR; j++) {
                        if (j == i) continue;
                        if (PlanetOwner(j+1) != NoOwner) {
                            if (DistanceSquared(x, y, PlanetLocationX(j+1), PlanetLocationY(j+1)) < distSq) {
                                valid = False;
                                break;
                            }
                        }
                    }
                }
                if (valid)
                    planets[candidates++] = i;
            }
        }

        if (candidates < 1)
            ErrorExit("Unable to find a valid planet for the device.");
            
        tantPlanet = planets[RandomRange(candidates)];
    }
}

/**************************************************************************************************************/
/** Write Tantalus data */
void
WriteTantalusData(void)
{
    Boolean error = False;
    FILE *F;
    F = OpenOutputFile("tantalus.hst", GAME_DIR_ONLY | NO_MISSING_ERROR);
    if (F != NULL) {
        Info("Writing tantalus data to file.");
        
        if (!DOSWrite16(&tantPlanet, 1, F))
            error = True;
        if (fputc(tantOwner, F) == EOF)
            error = True;
        if (!DOSWrite16(&turnsOwned, 1, F))
            error = True;

        fclose(F);
    } else
        error = True;
        
    if (error)        
        ErrorExit("Unable to write tantalus data.");
}            


/**************************************************************************************************************/
/** Help screen. */
void
Help(void)
{
    fprintf(stderr,
            "VGA Planets Tantalus Addon\n\n"
            "usage: %s [-hr] gamedir [rootdir]\n\n"
            " -h   this help screen\n"
            " -r   read-only\n"
            " -v   verbose: more messages\n",
            sProgName);
    exit(1);
}

/**************************************************************************************************************/
/** Send out a message to every player in the game if the device has been found. */
void
SendTantalusMessage(void)
{
    int x;
    char message[420];

    Info("Sending out tantalus message.");
    if (tantOwner != NoOwner) {
        for (x = 1; x <= RACE_NR; x++) {
            if (PlayerIsActive(x)) {
                if (turnsOwned < config.turnsOwned) {
                    char *is = "are";
                    if (config.turnsOwned - turnsOwned == 1)
                        is = "is";
                    sprintf(message,
                        "<<< Tantalus Device Captured >>>\r\r"
                        "%s have captured the\r"
                        "device on planet %d at (%d , %d)\r\r"
                        "They have owned it for %d turn%s\r\r"
                        "There %s %d turn%s remaining before\r"
                        "they activate it, and the game is over.\r",
                        RaceShortName(tantOwner, NULL),
                        tantPlanet+1,
                        PlanetLocationX(tantPlanet+1),
                        PlanetLocationY(tantPlanet+1),
                        turnsOwned,
                        turnsOwned!=1 ? "s" : "",
                        is,
                        config.turnsOwned - turnsOwned,
                        (config.turnsOwned - turnsOwned) != 1 ? "s" : "");
                } else {
                    sprintf(message,
                        "<<< Tantalus Device Activated >>>\r\r"
                        "Game Over\r\r"
                        "%s have activated\rthe Tantalus device.\r",
                        RaceShortName(tantOwner, NULL));
                }    
                WriteAUXHOSTMessage(x, message);
            }    
        }
    }    

    return;
}

/**************************************************************************************************************/
/** Main. */
int main(int argc, char *argv[])
{
    int c;
    sProgName = argv[0];

    /* Get command-line options */
    while ((c = pgetopt(argc, argv, "hrv")) != EOF) {
        if (c == 'h')
            Help();
        else if (c == 'r')
            sReadonly = 1;
        else if (c == 'v')
            sVerbose = 1;
        else
            ErrorExit("Invalid option '%c'.", c);
    }

    if (poptind < argc)
        gGameDirectory = argv[poptind++];
    if (poptind < argc)
        gRootDirectory = argv[poptind++];
    if (poptind < argc)
        ErrorExit("Too many parameters.");

    /* Here comes da fun */
    InitPHOSTLib();

    gLogFile = OpenOutputFile("tantalus.log", GAME_DIR_ONLY | TEXT_MODE);

    SetRandomSeed((Uns16) time(0));

    Info("Reading Tantalus data...");

    if (!ReadGlobalData())
        ErrorExit("Unable to read global data.");
    if (!ReadHostData())
        ErrorExit("Unable to read host data.");

    InitializeTantalus();
    ProcessTantalus();
    if (sVerbose)
        InfoTantalus();

    /* write out everything */
    if (!sReadonly) {
        WriteTantalusData();
        SendTantalusMessage();
    }

    Info("Tantalus finished successfully.");

    if (gLogFile != NULL) {
        fclose(gLogFile);
    }

    FreePHOSTLib();
    return EXIT_SUCCESS;
}
